home *** CD-ROM | disk | FTP | other *** search
/ PC World Komputer 2010 April / PCWorld0410.iso / pluginy Firefox / 1843 / 1843.xpi / content / firebug / inspector.js < prev    next >
Text File  |  2010-01-15  |  40KB  |  1,262 lines

  1. /* See license.txt for terms of usage */
  2.  
  3. FBL.ns(function() { with (FBL) {
  4.  
  5. // ************************************************************************************************
  6. // Constants
  7.  
  8. const inspectDelay = 100;
  9. const edgeSize = 2;
  10. const defaultPrimaryPanel = "html";
  11. const defaultSecondaryPanel = "dom";
  12. const highlightCSS = "chrome://firebug/content/highlighter.css";
  13.  
  14. // ************************************************************************************************
  15. // Globals
  16.  
  17. var boxModelHighlighter = null,
  18.     frameHighlighter = null,
  19.     popupHighlighter = null,
  20.     mx, my;
  21.  
  22. // ************************************************************************************************
  23.  
  24. Firebug.Inspector = extend(Firebug.Module,
  25. {
  26.     dispatchName: "inspector",
  27.     inspecting: false,
  28.  
  29.     highlightObject: function(element, context, highlightType, boxFrame)
  30.     {
  31.         if(context && context.window && context.window.document)
  32.         {
  33.             context.window.document.addEventListener("mousemove", function(event)
  34.             {
  35.                 mx = event.clientX;
  36.                 my = event.clientY;
  37.             }, true);
  38.         }
  39.  
  40.         if (!element || !isElement(element) || !isVisible(element))
  41.         {
  42.             if(element && element.nodeType == 3)
  43.                 element = element.parentNode;
  44.             else
  45.                 element = null;
  46.         }
  47.  
  48.         if (element && context && context.highlightTimeout)
  49.         {
  50.             context.clearTimeout(context.highlightTimeout);
  51.             delete context.highlightTimeout;
  52.         }
  53.  
  54.         var highlighter = highlightType ? getHighlighter(highlightType) : this.defaultHighlighter;
  55.  
  56.         var oldContext = this.highlightedContext;
  57.         if (oldContext && highlighter != this.highlighter)
  58.         {
  59.             if (oldContext.window)
  60.                 this.highlighter.unhighlight(oldContext);
  61.         }
  62.  
  63.         this.highlighter = highlighter;
  64.         this.highlightedElement = element;
  65.         this.highlightedContext = context;
  66.  
  67.         if (element)
  68.         {
  69.             if(!isVisibleElement(element))
  70.                 highlighter.unhighlight(context);
  71.             else if (context && context.window && context.window.document)
  72.                 highlighter.highlight(context, element, boxFrame);
  73.         }
  74.         else if (oldContext)
  75.         {
  76.             oldContext.highlightTimeout = oldContext.setTimeout(function()
  77.             {
  78.                 delete oldContext.highlightTimeout;
  79.                 if (oldContext.window && oldContext.window.document)
  80.                     highlighter.unhighlight(oldContext);
  81.             }, inspectDelay);
  82.         }
  83.     },
  84.  
  85.     toggleInspecting: function(context)
  86.     {
  87.         if (this.inspecting)
  88.             this.stopInspecting(true);
  89.         else
  90.             this.startInspecting(context);
  91.     },
  92.  
  93.     startInspecting: function(context)
  94.     {
  95.         if (this.inspecting || !context || !context.loaded)
  96.             return;
  97.  
  98.         this.inspecting = true;
  99.         this.inspectingContext = context;
  100.  
  101.         Firebug.chrome.setGlobalAttribute("cmd_toggleInspecting", "checked", "true");
  102.         this.attachInspectListeners(context);
  103.  
  104.         var htmlPanel = Firebug.chrome.switchToPanel(context, "html");
  105.  
  106.         if (Firebug.isDetached())
  107.             context.window.focus();
  108.         else if (Firebug.isMinimized())
  109.             Firebug.showBar(true);
  110.  
  111.         htmlPanel.panelNode.focus();
  112.         htmlPanel.startInspecting();
  113.  
  114.         if (context.stopped)
  115.             Firebug.Debugger.thaw(context);
  116.  
  117.         if (context.hoverNode)
  118.             this.inspectNode(context.hoverNode);
  119.     },
  120.  
  121.     inspectNode: function(node)
  122.     {
  123.         if (node && node.nodeType != 1)
  124.             node = node.parentNode;
  125.  
  126.         if (node && node.firebugIgnore && !node.fbProxyFor)
  127.             return;
  128.  
  129.         var context = this.inspectingContext;
  130.  
  131.         if (this.inspectTimeout)
  132.         {
  133.             context.clearTimeout(this.inspectTimeout);
  134.             delete this.inspectTimeout;
  135.         }
  136.  
  137.         if(node && node.fbProxyFor)
  138.             node = node.fbProxyFor;
  139.  
  140.         this.highlightObject(node, context, "frame");
  141.  
  142.         this.inspectingNode = node;
  143.  
  144.         if (node)
  145.         {
  146.             this.inspectTimeout = context.setTimeout(function()
  147.             {
  148.                 Firebug.chrome.select(node);
  149.             }, inspectDelay);
  150.             dispatch(this.fbListeners, "onInspectNode", [context, node] );
  151.         }
  152.     },
  153.  
  154.     stopInspecting: function(cancelled, waitForClick)
  155.     {
  156.         if (!this.inspecting)
  157.             return;
  158.  
  159.         var context = this.inspectingContext;
  160.  
  161.         if (context.stopped)
  162.             Firebug.Debugger.freeze(context);
  163.  
  164.         if (this.inspectTimeout)
  165.         {
  166.             context.clearTimeout(this.inspectTimeout);
  167.             delete this.inspectTimeout;
  168.         }
  169.  
  170.         this.detachInspectListeners(context);
  171.         if (!waitForClick)
  172.             this.detachClickInspectListeners(context.window);
  173.  
  174.         Firebug.chrome.setGlobalAttribute("cmd_toggleInspecting", "checked", "false");
  175.  
  176.         this.inspecting = false;
  177.  
  178.         var htmlPanel = Firebug.chrome.unswitchToPanel(context, "html", cancelled);
  179.  
  180.         htmlPanel.stopInspecting(htmlPanel.selection, cancelled);
  181.  
  182.         dispatch(this.fbListeners, "onStopInspecting", [context] );
  183.  
  184.         this.inspectNode(null);
  185.     },
  186.     
  187.     inspectFromContextMenu: function(elt)
  188.     {
  189.         var context, htmlPanel;
  190.  
  191.         Firebug.toggleBar(true);
  192.         Firebug.chrome.select(elt, "html");
  193.         context = this.inspectingContext || TabWatcher.getContextByWindow(elt.ownerDocument.defaultView);
  194.         htmlPanel = Firebug.chrome.unswitchToPanel(context, "html", false);
  195.         htmlPanel.panelNode.focus();
  196.     },
  197.     
  198.     inspectNodeBy: function(dir)
  199.     {
  200.         var target;
  201.         var node = this.inspectingNode;
  202.  
  203.         if (dir == "up")
  204.             target = Firebug.chrome.getNextObject();
  205.         else if (dir == "down")
  206.         {
  207.             target = Firebug.chrome.getNextObject(true);
  208.             if (node && !target)
  209.             {
  210.                 if (node.contentDocument)
  211.                     target = node.contentDocument.documentElement;
  212.                 else
  213.                     target = getNextElement(node.firstChild);
  214.             }
  215.         }
  216.  
  217.         if (target && isElement(target))
  218.             this.inspectNode(target);
  219.         else
  220.             beep();
  221.     },
  222.  
  223.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  224.  
  225.     attachInspectListeners: function(context)
  226.     {
  227.         var win = context.window;
  228.         if (!win || !win.document)
  229.             return;
  230.  
  231.         var chrome = Firebug.chrome;
  232.  
  233.         this.keyListeners =
  234.         [
  235.             chrome.keyCodeListen("RETURN", null, bindFixed(this.stopInspecting, this)),
  236.             chrome.keyCodeListen("ESCAPE", null, bindFixed(this.stopInspecting, this, true)),
  237.             chrome.keyCodeListen("UP", isControl, bindFixed(this.inspectNodeBy, this, "up"), true),
  238.             chrome.keyCodeListen("DOWN", isControl, bindFixed(this.inspectNodeBy, this, "down"), true),
  239.         ];
  240.  
  241.         iterateWindows(win, bind(function(subWin)
  242.         {
  243.             subWin.document.addEventListener("mouseover", this.onInspectingMouseOver, true);
  244.             subWin.document.addEventListener("mousedown", this.onInspectingMouseDown, true);
  245.             subWin.document.addEventListener("click", this.onInspectingClick, true);
  246.         }, this));
  247.     },
  248.  
  249.     detachInspectListeners: function(context)
  250.     {
  251.         var i, keyListenersLen,
  252.             win = context.window;
  253.  
  254.         if (!win || !win.document)
  255.             return;
  256.  
  257.         var chrome = Firebug.chrome;
  258.  
  259.         if (this.keyListeners)  // XXXjjb for some reason this is null some times.
  260.         {
  261.             keyListenersLen = this.keyListeners.length;
  262.             for (i = 0; i < keyListenersLen; ++i)
  263.                 chrome.keyIgnore(this.keyListeners[i]);
  264.             delete this.keyListeners;
  265.         }
  266.  
  267.         iterateWindows(win, bind(function(subWin)
  268.         {
  269.             subWin.document.removeEventListener("mouseover", this.onInspectingMouseOver, true);
  270.             subWin.document.removeEventListener("mousedown", this.onInspectingMouseDown, true);
  271.         }, this));
  272.     },
  273.  
  274.     detachClickInspectListeners: function(win)
  275.     {
  276.         // We have to remove the click listener in a second phase because if we remove it
  277.         // after the mousedown, we won't be able to cancel clicked links
  278.         iterateWindows(win, bind(function(subWin)
  279.         {
  280.             subWin.document.removeEventListener("click", this.onInspectingClick, true);
  281.         }, this));
  282.     },
  283.  
  284.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  285.  
  286.     onInspectingMouseOver: function(event)
  287.     {
  288.         this.inspectNode(event.target);
  289.         cancelEvent(event);
  290.     },
  291.  
  292.     onInspectingMouseDown: function(event)
  293.     {
  294.         this.stopInspecting(false, true);
  295.         cancelEvent(event);
  296.     },
  297.  
  298.     onInspectingClick: function(event)
  299.     {
  300.         var win = event.currentTarget.defaultView;
  301.         if (win)
  302.         {
  303.             win = getRootWindow(win);
  304.             this.detachClickInspectListeners(win);
  305.         }
  306.         cancelEvent(event);
  307.     },
  308.  
  309.     // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  310.     // extends Module
  311.  
  312.     initialize: function()
  313.     {
  314.         Firebug.Module.initialize.apply(this, arguments);
  315.  
  316.         this.onInspectingMouseOver = bind(this.onInspectingMouseOver, this);
  317.         this.onInspectingMouseDown = bind(this.onInspectingMouseDown, this);
  318.         this.onInspectingClick = bind(this.onInspectingClick, this);
  319.  
  320.         this.updateOption("shadeBoxModel", Firebug.shadeBoxModel);
  321.         this.updateOption("showQuickInfoBox", Firebug.showQuickInfoBox);
  322.     },
  323.  
  324.     initContext: function(context)
  325.     {
  326.         context.onPreInspectMouseOver = function(event) { context.hoverNode = event.target; };
  327.     },
  328.  
  329.     destroyContext: function(context)
  330.     {
  331.         if (context.highlightTimeout)
  332.         {
  333.             context.clearTimeout(context.highlightTimeout);
  334.             delete context.highlightTimeout;
  335.         }
  336.  
  337.         if (this.inspecting)
  338.             this.stopInspecting(true);
  339.     },
  340.  
  341.     watchWindow: function(context, win)
  342.     {
  343.         win.addEventListener("mouseover", context.onPreInspectMouseOver, true);
  344.     },
  345.  
  346.     unwatchWindow: function(context, win)
  347.     {
  348.         try {
  349.             win.removeEventListener("mouseover", context.onPreInspectMouseOver, true);
  350.             this.hideQuickInfoBox();
  351.         } catch (ex) {
  352.             // Get unfortunate errors here sometimes, so let's just ignore them
  353.             // since the window is going away anyhow
  354.         }
  355.     },
  356.  
  357.     showContext: function(browser, context)
  358.     {
  359.         if (this.inspecting)
  360.             this.stopInspecting(true);
  361.     },
  362.  
  363.     showPanel: function(browser, panel)
  364.     {
  365.         var chrome = Firebug.chrome;
  366.         var disabled = !panel || !panel.context.loaded;
  367.  
  368.         chrome.setGlobalAttribute("cmd_toggleInspecting", "disabled", disabled);
  369.         //chrome.setGlobalAttribute("menu_firebugInspect", "disabled", disabled);
  370.     },
  371.  
  372.     loadedContext: function(context)
  373.     {
  374.         Firebug.chrome.setGlobalAttribute("cmd_toggleInspecting", "disabled", "false");
  375.         //Firebug.chrome.setGlobalAttribute("menu_firebugInspect", "disabled", "false");
  376.     },
  377.  
  378.     updateOption: function(name, value)
  379.     {
  380.         if (name == "shadeBoxModel")
  381.             {
  382.             this.highlightObject(null);
  383.             this.defaultHighlighter = value ? getHighlighter("boxModel") : getHighlighter("frame");
  384.             }
  385.         else if(name == "showQuickInfoBox")
  386.             quickInfoBox.boxEnabled = value;
  387.     },
  388.  
  389.     getObjectByURL: function(context, url)
  390.     {
  391.         var styleSheet = getStyleSheetByHref(url, context);
  392.         if (styleSheet)
  393.             return styleSheet;
  394.  
  395.         /*var path = getURLPath(url);
  396.         var xpath = "//*[contains(@src, '" + path + "')]";
  397.         var elements = getElementsByXPath(context.window.document, xpath);
  398.         if (elements.length)
  399.             return elements[0];*/
  400.     },
  401.  
  402.     toggleQuickInfoBox: function()
  403.     {
  404.         var qiBox = $('fbQuickInfoPanel');
  405.  
  406.         if (qiBox.state==="open")
  407.             quickInfoBox.hide();
  408.  
  409.         quickInfoBox.boxEnabled = !quickInfoBox.boxEnabled;
  410.  
  411.         Firebug.setPref(Firebug.prefDomain, "showQuickInfoBox", quickInfoBox.boxEnabled);
  412.     },
  413.  
  414.     hideQuickInfoBox: function()
  415.     {
  416.         var qiBox = $('fbQuickInfoPanel');
  417.  
  418.         if (qiBox.state==="open")
  419.             quickInfoBox.hide();
  420.  
  421.         this.inspectNode(null);
  422.     },
  423.  
  424.     quickInfoBoxDragStart: function(event)
  425.     {
  426.         quickInfoBox.dragStart(event);
  427.     },
  428.  
  429.     quickInfoBoxDrag: function(event)
  430.     {
  431.         quickInfoBox.drag(event);
  432.     },
  433.  
  434.     quickInfoBoxDragEnd: function(event)
  435.     {
  436.         quickInfoBox.dragEnd(event);
  437.     }
  438. });
  439.  
  440. // ************************************************************************************************
  441. // Local Helpers
  442.  
  443. function getHighlighter(type)
  444. {
  445.     if (type == "boxModel")
  446.     {
  447.         if (!boxModelHighlighter)
  448.             boxModelHighlighter = new BoxModelHighlighter();
  449.  
  450.         return boxModelHighlighter;
  451.     }
  452.     else if (type == "frame")
  453.     {
  454.         if (!frameHighlighter)
  455.             frameHighlighter = new Firebug.Inspector.FrameHighlighter();
  456.  
  457.         return frameHighlighter;
  458.     }
  459.     else if (type == "popup")
  460.     {
  461.         if (!popupHighlighter)
  462.             popupHighlighter = new PopupHighlighter();
  463.  
  464.         return popupHighlighter;
  465.     }
  466. }
  467.  
  468. function pad(element, t, r, b, l)
  469. {
  470.     element.style.padding = Math.abs(t) + "px " + Math.abs(r) + "px "
  471.         + Math.abs(b) + "px " + Math.abs(l) + "px";
  472. }
  473.  
  474. // ************************************************************************************************
  475. // Imagemap Inspector
  476.  
  477. function getImageMapHighlighter(context)
  478. {
  479.     if(!context)
  480.         return;
  481.  
  482.     var canvas, ctx,
  483.         doc = context.window.document,
  484.         init = function(elt)
  485.         {
  486.             if(elt)
  487.                 doc = elt.ownerDocument;
  488.  
  489.             canvas = doc.getElementById('firebugCanvas');
  490.  
  491.             if(!canvas)
  492.             {
  493.                 canvas = doc.createElementNS("http://www.w3.org/1999/xhtml", "canvas");
  494.                 canvas.wrappedJSObject.firebugIgnore = true;
  495.                 canvas.id = "firebugCanvas";
  496.                 canvas.className = "firebugCanvas";
  497.                 canvas.width = context.window.innerWidth;
  498.                 canvas.height = context.window.innerHeight;
  499.                 canvas.addEventListener("mousemove", function(event){context.imageMapHighlighter.mouseMoved(event)}, true);
  500.                 canvas.addEventListener("mouseout", function(){getImageMapHighlighter(context).destroy();}, true);
  501.                 context.window.addEventListener("scroll", function(){context.imageMapHighlighter.show(false);}, true);
  502.  
  503.                 doc.body.appendChild(canvas);
  504.             }
  505.         };
  506.  
  507.     if (!context.imageMapHighlighter)
  508.     {
  509.         context.imageMapHighlighter =
  510.         {
  511.             show: function(state)
  512.             {
  513.                 if(!canvas)
  514.                     init();
  515.  
  516.                 canvas.style.display = state?'block':'none';
  517.             },
  518.  
  519.             getImages: function(mapName, multi)
  520.             {
  521.                 var i, eltsLen,
  522.                     elts = [],
  523.                     images = [],
  524.                     elts2 = doc.getElementsByTagName("img"),
  525.                     elts3 = doc.getElementsByTagName("input"),
  526.                     elts2Len = elts2.length,
  527.                     elts3Len = elts3.length;
  528.  
  529.                 for(i = 0; i < elts2Len; i++)
  530.                     elts.push(elts2[i]);
  531.  
  532.                 for(i = 0; i < elts3Len; i++)
  533.                     elts.push(elts3[i]);
  534.  
  535.                 if(elts)
  536.                 {
  537.                     eltsLen = elts.length;
  538.  
  539.                     for(i = 0; i < eltsLen; i++)
  540.                     {
  541.                         if(elts[i].getAttribute('usemap') == mapName)
  542.                         {
  543.                             rect = getLTRBWH(elts[i]);
  544.  
  545.                             if(multi)
  546.                                 images.push(elts[i]);
  547.                             else if(rect.left <= mx && rect.right >= mx && rect.top <= my && rect.bottom >= my)
  548.                             {
  549.                                 images[0] = elts[i];
  550.                                 break;
  551.                             }
  552.                         }
  553.                     }
  554.                 }
  555.                 return images;
  556.             },
  557.  
  558.             highlight: function(eltArea, multi)
  559.             {
  560.                 var i, j, v, vLen, images, imagesLen, rect, shape, clearForFirst;
  561.  
  562.                 if (eltArea && eltArea.coords)
  563.                 {
  564.                     images = this.getImages("#"+eltArea.parentNode.name, multi);
  565.  
  566.                     init(eltArea);
  567.  
  568.                     v = eltArea.coords.split(",");
  569.  
  570.                     if(!ctx)
  571.                         ctx = canvas.getContext("2d");
  572.  
  573.                     ctx.fillStyle = "rgba(135, 206, 235, 0.7)";
  574.                     ctx.strokeStyle = "rgb(29, 55, 95)";
  575.                     ctx.lineWidth = 2;
  576.  
  577.                     if(images.length === 0)
  578.                         images[0] = eltArea;
  579.  
  580.                     imagesLen = images.length;
  581.  
  582.                     for(j = 0; j < imagesLen; j++)
  583.                     {
  584.                         rect = getLTRBWH(images[j], context);
  585.  
  586.                         ctx.beginPath();
  587.  
  588.                         if(!multi || (multi && j===0))
  589.                             ctx.clearRect(0, 0, canvas.width, canvas.height);
  590.  
  591.                         shape = eltArea.shape.toLowerCase();
  592.  
  593.                         if (shape === 'rect')
  594.                             ctx.rect(rect.left + parseInt(v[0], 10), rect.top + parseInt(v[1], 10), v[2] - v[0], v[3] - v[1]);
  595.                         else if (shape === 'circle')
  596.                             ctx.arc(rect.left + parseInt(v[0], 10) + ctx.lineWidth / 2, rect.top + parseInt(v[1], 10) + ctx.lineWidth / 2, v[2], 0, Math.PI / 180 * 360, false);
  597.                         else
  598.                         {
  599.                             vLen = v.length;
  600.                             ctx.moveTo(rect.left + parseInt(v[0], 10), rect.top + parseInt(v[1], 10));
  601.                             for(i=2; i < vLen; i += 2)
  602.                                 ctx.lineTo(rect.left + parseInt(v[i], 10), rect.top + parseInt(v[i + 1], 10));
  603.                         }
  604.  
  605.                         ctx.fill();
  606.                         ctx.stroke();
  607.                         ctx.closePath();
  608.                     }
  609.  
  610.                     this.show(true);
  611.                 }
  612.                 else
  613.                     return;
  614.             },
  615.  
  616.             mouseMoved: function(event)
  617.             {
  618.                 var idata = ctx.getImageData(event.layerX, event.layerY, 1, 1);
  619.  
  620.                 mx = event.clientX;
  621.                 my = event.clientY;
  622.  
  623.                 if (!idata || (idata.data[0] === 0 && idata.data[1] === 0 && idata.data[2] === 0 && idata.data[3] === 0))
  624.                     this.show(false);
  625.             },
  626.  
  627.             destroy: function()
  628.             {
  629.                 canvas = null;
  630.                 ctx = null;
  631.             }
  632.         }
  633.     }
  634.  
  635.     return context.imageMapHighlighter;
  636. }
  637.  
  638. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  639. quickInfoBox =
  640. {
  641.     boxEnabled: undefined,
  642.     dragging: false,
  643.     storedX: null,
  644.     storedY: null,
  645.     prevX: null,
  646.     prevY: null,
  647.  
  648.     show: function(element)
  649.     {
  650.         if (!this.boxEnabled || !element)
  651.             return;
  652.  
  653.         var vbox, lab,
  654.             needsTitle = false,
  655.             needsTitle2 = false,
  656.             domAttribs = ['nodeName', 'id', 'name', 'offsetWidth', 'offsetHeight'],
  657.             cssAttribs = ['position'],
  658.             compAttribs = ['width', 'height', 'zIndex', 'position', 'top', 'right', 'bottom', 'left',
  659.                            'margin-top', 'margin-right', 'margin-bottom', 'margin-left', 'color', 'backgroundColor',
  660.                            'fontFamily', 'cssFloat', 'display', 'visibility'],
  661.             qiBox = $('fbQuickInfoPanel');
  662.  
  663.         if (qiBox.state==="closed")
  664.         {
  665.             qiBox.hidePopup();
  666.  
  667.             this.storedX = this.storedX || $('content').tabContainer.boxObject.screenX + 5;
  668.             this.storedY = this.storedY || $('content').tabContainer.boxObject.screenY + 35;
  669.  
  670.             qiBox.openPopupAtScreen(this.storedX, this.storedY, false);
  671.         }
  672.  
  673.         qiBox.removeChild(qiBox.firstChild);
  674.         vbox = document.createElement("vbox");
  675.         qiBox.appendChild(vbox);
  676.  
  677.         needsTitle = this.addRows(element, vbox, domAttribs);
  678.         needsTitle2 = this.addRows(element.style, vbox, cssAttribs);
  679.  
  680.         if (needsTitle || needsTitle2)
  681.         {
  682.             lab = document.createElement("label");
  683.             lab.setAttribute("class", "fbQuickInfoBoxTitle");
  684.             lab.setAttribute("value", $STR("quickInfo"));
  685.             vbox.insertBefore(lab, vbox.firstChild);
  686.         }
  687.  
  688.         lab = document.createElement("label");
  689.         lab.setAttribute("class", "fbQuickInfoBoxTitle");
  690.         lab.setAttribute("value", $STR("computedStyle"));
  691.         vbox.appendChild(lab);
  692.  
  693.         this.addRows(element, vbox, compAttribs, true);
  694.     },
  695.  
  696.     hide: function()
  697.     {
  698.         this.prevX = null;
  699.         this.prevY = null;
  700.         qiBox = $('fbQuickInfoPanel');
  701.         qiBox.hidePopup();
  702.     },
  703.  
  704.     dragStart: function(event)
  705.     {
  706.         this.dragging = true;
  707.     },
  708.  
  709.     dragEnd: function(event)
  710.     {
  711.         this.dragging = false;
  712.         this.prevX = null;
  713.         this.prevY = null;
  714.     },
  715.  
  716.     drag: function(event)
  717.     {
  718.         if(this.dragging)
  719.         {
  720.             var diffX, diffY, newX, newY,
  721.                 qiBox = $('fbQuickInfoPanel'),
  722.                 boxX = qiBox.boxObject.screenX,
  723.                 boxY = qiBox.boxObject.screenY,
  724.                 x = event.screenX,
  725.                 y = event.screenY;
  726.  
  727.             this.prevX = this.prevX || x;
  728.             this.prevY = this.prevY || y;
  729.             diffX = x - this.prevX;
  730.             diffY = y - this.prevY;
  731.             newX = boxX + diffX;
  732.             newY = boxY + diffY;
  733.  
  734.             if(newX < 0)
  735.                 newX = 0;
  736.  
  737.             if(newY < 0)
  738.                 newY = 0;
  739.  
  740.             if(newY + qiBox.boxObject.height > window.screen.height - 5)
  741.                 newY = window.screen.height - qiBox.boxObject.height - 5;
  742.  
  743.             qiBox.hidePopup();
  744.             qiBox.openPopupAtScreen(newX, newY, false);
  745.  
  746.             this.prevX = x;
  747.             this.prevY = y;
  748.             this.storedX = boxX;
  749.             this.storedY = boxY;
  750.         }
  751.     },
  752.  
  753.     addRows: function(domBase, vbox, attribs, computedStyle)
  754.     {
  755.         if(!domBase)
  756.             return;
  757.  
  758.         var i, cs, desc, hbox, lab, value,
  759.             needsTitle = false,
  760.             attribsLen = attribs.length;
  761.  
  762.         for (i = 0; i < attribsLen; i++)
  763.         {
  764.             if(computedStyle)
  765.             {
  766.                 cs = getNonFrameBody(domBase).ownerDocument.defaultView.getComputedStyle(domBase, null);
  767.                 value = cs.getPropertyValue(attribs[i]);
  768.  
  769.                 if (value && /rgb\(\d+,\s\d+,\s\d+\)/.test(value))
  770.                     value = rgbToHex(value);
  771.             }
  772.             else
  773.                 value = domBase[attribs[i]];
  774.  
  775.             if (value)
  776.             {
  777.                 needsTitle = true;
  778.                 hbox = document.createElement("hbox");
  779.                 lab = document.createElement("label");
  780.                 lab.setAttribute("class", "fbQuickInfoName");
  781.                 lab.setAttribute("value", attribs[i]);
  782.                 hbox.appendChild(lab);
  783.                 desc = document.createElement("description");
  784.                 desc.setAttribute("class", "fbQuickInfoValue");
  785.                 desc.appendChild(document.createTextNode(": " + value));
  786.                 hbox.appendChild(desc);
  787.                 vbox.appendChild(hbox);
  788.             }
  789.         }
  790.  
  791.         return needsTitle;
  792.     }
  793. }
  794.  
  795. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  796.  
  797. Firebug.Inspector.FrameHighlighter = function()
  798. {
  799. }
  800.  
  801. Firebug.Inspector.FrameHighlighter.prototype =
  802. {
  803.     doNotHighlight: function(element)
  804.     {
  805.         return false; // (element instanceof XULElement);
  806.     },
  807.  
  808.     highlight: function(context, element)
  809.     {
  810.         if (this.doNotHighlight(element))
  811.             return;
  812.  
  813.         var offset = getLTRBWH(element);
  814.         offset = applyBodyOffsets(element, offset);
  815.         var x = offset.left, y = offset.top;
  816.         var w = offset.width, h = offset.height;
  817.         var wacked = isNaN(x) || isNaN(y) || isNaN(w) || isNaN(h);
  818.         if (wacked)
  819.             return;
  820.  
  821.         if(element.tagName !== "AREA")
  822.         {
  823.             quickInfoBox.show(element);
  824.  
  825.             var nodes = this.getNodes(context, element);
  826.  
  827.             move(nodes.top, x, y-edgeSize);
  828.             resize(nodes.top, w, edgeSize);
  829.  
  830.             move(nodes.right, x+w, y-edgeSize);
  831.             resize(nodes.right, edgeSize, h+edgeSize*2);
  832.  
  833.             move(nodes.bottom, x, y+h);
  834.             resize(nodes.bottom, w, edgeSize);
  835.  
  836.             move(nodes.left, x-edgeSize, y-edgeSize);
  837.             resize(nodes.left, edgeSize, h+edgeSize*2);
  838.             var body = getNonFrameBody(element);
  839.             if (!body)
  840.                 return this.unhighlight(context);
  841.  
  842.             var needsAppend = !nodes.top.parentNode || nodes.top.ownerDocument != body.ownerDocument;
  843.             if (needsAppend)
  844.             {
  845.                 attachStyles(context, body);
  846.                 for (var edge in nodes)
  847.                 {
  848.                     try
  849.                     {
  850.                         body.appendChild(nodes[edge]);
  851.                     }
  852.                     catch(exc)
  853.                     {
  854.                     }
  855.                 }
  856.  
  857.                 createProxiesForDisabledElements(body);
  858.             }
  859.         }
  860.         else
  861.         {
  862.             var ihl = getImageMapHighlighter(context);
  863.             ihl.highlight(element, false);
  864.         }
  865.     },
  866.  
  867.     unhighlight: function(context)
  868.     {
  869.         var nodes = this.getNodes(context);
  870.         var body = nodes.top.parentNode;
  871.         if (body)
  872.         {
  873.             for (var edge in nodes)
  874.                 body.removeChild(nodes[edge]);
  875.  
  876.             quickInfoBox.hide();
  877.         }
  878.     },
  879.  
  880.     getNodes: function(context)
  881.     {
  882.         if (!context.frameHighlighter)
  883.         {
  884.             var doc = context.window.document;
  885.  
  886.             function createEdge(name)
  887.             {
  888.                 var div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
  889.                 unwrapObject(div).firebugIgnore = true;
  890.                 div.className = "firebugHighlight";
  891.                 return div;
  892.             }
  893.  
  894.             context.frameHighlighter =
  895.             {
  896.                 top: createEdge("Top"),
  897.                 right: createEdge("Right"),
  898.                 bottom: createEdge("Bottom"),
  899.                 left: createEdge("Left")
  900.             };
  901.         }
  902.  
  903.         return context.frameHighlighter;
  904.     }
  905. };
  906. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  907.  
  908. function PopupHighlighter()
  909. {
  910. }
  911.  
  912. PopupHighlighter.prototype =
  913. {
  914.     highlight: function(context, element)
  915.     {
  916.         var doc = context.window.document;
  917.         var popup = doc.getElementById("inspectorPopup");
  918.         popup.style.width = "200px";
  919.         popup.style.height = "100px";
  920.         popup.showPopup(element, element.boxObject.screenX,
  921.             element.boxObject.screenY, "popup", "none", "none");
  922.     },
  923.  
  924.     unhighlight: function(context)
  925.     {
  926.     },
  927. }
  928. // * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
  929.  
  930. function BoxModelHighlighter()
  931. {
  932. }
  933.  
  934. BoxModelHighlighter.prototype =
  935. {
  936.     highlight: function(context, element, boxFrame)
  937.     {
  938.         var nodes = this.getNodes(context);
  939.         var highlightFrame = boxFrame ? nodes[boxFrame] : null;
  940.  
  941.         if (context.highlightFrame)
  942.             removeClass(context.highlightFrame, "firebugHighlightBox");
  943.  
  944.         if(element.tagName !== "AREA")
  945.         {
  946.             quickInfoBox.show(element);
  947.             context.highlightFrame = highlightFrame;
  948.  
  949.             if (highlightFrame)
  950.             {
  951.                 setClass(nodes.offset, "firebugHighlightGroup");
  952.                 setClass(highlightFrame, "firebugHighlightBox");
  953.             }
  954.             else
  955.                 removeClass(nodes.offset, "firebugHighlightGroup");
  956.  
  957.             var win = element.ownerDocument.defaultView;
  958.             if (!win)
  959.                 return;
  960.  
  961.             var style = win.getComputedStyle(element, "");
  962.             if (!style)
  963.             {
  964.                 return;
  965.             }
  966.  
  967.             var styles = readBoxStyles(style);
  968.  
  969.             var offset = getLTRBWH(element);
  970.             offset = applyBodyOffsets(element, offset);
  971.  
  972.             var x = offset.left - Math.abs(styles.marginLeft);
  973.             var y = offset.top - Math.abs(styles.marginTop);
  974.             var w = offset.width - (styles.paddingLeft + styles.paddingRight
  975.                     + styles.borderLeft + styles.borderRight);
  976.             var h = offset.height - (styles.paddingTop + styles.paddingBottom
  977.                     + styles.borderTop + styles.borderBottom);
  978.  
  979.             move(nodes.offset, x, y);
  980.             pad(nodes.margin, styles.marginTop, styles.marginRight, styles.marginBottom,
  981.                     styles.marginLeft);
  982.             pad(nodes.border, styles.borderTop, styles.borderRight, styles.borderBottom,
  983.                     styles.borderLeft);
  984.             pad(nodes.padding, styles.paddingTop, styles.paddingRight, styles.paddingBottom,
  985.                     styles.paddingLeft);
  986.             resize(nodes.content, w, h);
  987.  
  988.             // element.tagName !== "BODY" for issue 2447. hopefully temporary, robc
  989.             var showLines = Firebug.showRulers && boxFrame && element.tagName !== "BODY";
  990.             if (showLines)
  991.             {
  992.                 var offsetParent = element.offsetParent;
  993.                 if (offsetParent)
  994.                     this.setNodesByOffsetParent(win, offsetParent, nodes);
  995.                 else
  996.                     delete nodes.parent;
  997.  
  998.                 var left = x;
  999.                 var top = y;
  1000.                 var width = w-1;
  1001.                 var height = h-1;
  1002.  
  1003.                 if (boxFrame == "content")
  1004.                 {
  1005.                     left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft)
  1006.                         + Math.abs(styles.paddingLeft);
  1007.                     top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop)
  1008.                         + Math.abs(styles.paddingTop);
  1009.                 }
  1010.                 else if (boxFrame == "padding")
  1011.                 {
  1012.                     left += Math.abs(styles.marginLeft) + Math.abs(styles.borderLeft);
  1013.                     top += Math.abs(styles.marginTop) + Math.abs(styles.borderTop);
  1014.                     width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight);
  1015.                     height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom);
  1016.                 }
  1017.                 else if (boxFrame == "border")
  1018.                 {
  1019.                     left += Math.abs(styles.marginLeft);
  1020.                     top += Math.abs(styles.marginTop);
  1021.                     width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight)
  1022.                          + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight);
  1023.                     height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom)
  1024.                         + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom);
  1025.                 }
  1026.                 else if (boxFrame == "margin")
  1027.                 {
  1028.                     width += Math.abs(styles.paddingLeft) + Math.abs(styles.paddingRight)
  1029.                          + Math.abs(styles.borderLeft) + Math.abs(styles.borderRight)
  1030.                          + Math.abs(styles.marginLeft) + Math.abs(styles.marginRight);
  1031.                     height += Math.abs(styles.paddingTop) + Math.abs(styles.paddingBottom)
  1032.                         + Math.abs(styles.borderTop) + Math.abs(styles.borderBottom)
  1033.                         + Math.abs(styles.marginTop) + Math.abs(styles.marginBottom);
  1034.                 }
  1035.  
  1036.                 move(nodes.lines.top, 0, top);
  1037.                 move(nodes.lines.right, left+width, 0);
  1038.                 move(nodes.lines.bottom, 0, top+height);
  1039.                 move(nodes.lines.left, left, 0)
  1040.             }
  1041.  
  1042.             var body = getNonFrameBody(element);
  1043.             if (!body)
  1044.                 return this.unhighlight(context);
  1045.  
  1046.             var needsAppend = !nodes.offset.parentNode
  1047.                 || nodes.offset.parentNode.ownerDocument != body.ownerDocument;
  1048.  
  1049.             if (needsAppend)
  1050.             {
  1051.                 attachStyles(context, body);
  1052.                 body.appendChild(nodes.offset);
  1053.             }
  1054.  
  1055.             if (showLines)
  1056.             {
  1057.                 if (!nodes.lines.top.parentNode)
  1058.                 {
  1059.                     if (nodes.parent)
  1060.                         body.appendChild(nodes.parent);
  1061.  
  1062.                     for (var line in nodes.lines)
  1063.                         body.appendChild(nodes.lines[line]);
  1064.                 }
  1065.             }
  1066.             else if (nodes.lines.top.parentNode)
  1067.             {
  1068.                 if (nodes.parent)
  1069.                     body.removeChild(nodes.parent);
  1070.  
  1071.                 for (var line in nodes.lines)
  1072.                     body.removeChild(nodes.lines[line]);
  1073.             }
  1074.         }
  1075.         else
  1076.         {
  1077.             var ihl = getImageMapHighlighter(context);
  1078.             ihl.highlight(element, true);
  1079.         }
  1080.     },
  1081.  
  1082.     unhighlight: function(context)
  1083.     {
  1084.         var nodes = this.getNodes(context);
  1085.         if (nodes.offset.parentNode)
  1086.         {
  1087.             var body = nodes.offset.parentNode;
  1088.             body.removeChild(nodes.offset);
  1089.  
  1090.             if (nodes.lines.top.parentNode)
  1091.             {
  1092.                 if (nodes.parent)
  1093.                     body.removeChild(nodes.parent);
  1094.  
  1095.                 for (var line in nodes.lines)
  1096.                     body.removeChild(nodes.lines[line]);
  1097.             }
  1098.         }
  1099.  
  1100.         quickInfoBox.hide();
  1101.     },
  1102.  
  1103.     getNodes: function(context)
  1104.     {
  1105.         if (!context.boxModelHighlighter)
  1106.         {
  1107.             var doc = context.window.document;
  1108.             function createRuler(name)
  1109.             {
  1110.                 var div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
  1111.                 unwrapObject(div).firebugIgnore = true;
  1112.                 div.className = "firebugRuler firebugRuler"+name;
  1113.                 return div;
  1114.             }
  1115.  
  1116.             function createBox(name)
  1117.             {
  1118.                 var div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
  1119.                 unwrapObject(div).firebugIgnore = true;
  1120.                 div.className = "firebugLayoutBox firebugLayoutBox"+name;
  1121.                 return div;
  1122.             }
  1123.  
  1124.             function createLine(name)
  1125.             {
  1126.                 var div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
  1127.                 unwrapObject(div).firebugIgnore = true;
  1128.                 div.className = "firebugLayoutLine firebugLayoutLine"+name;
  1129.                 return div;
  1130.             }
  1131.  
  1132.             var nodes = context.boxModelHighlighter =
  1133.             {
  1134.                 parent: createBox("Parent"),
  1135.                 rulerH: createRuler("H"),
  1136.                 rulerV: createRuler("V"),
  1137.                 offset: createBox("Offset"),
  1138.                 margin: createBox("Margin"),
  1139.                 border: createBox("Border"),
  1140.                 padding: createBox("Padding"),
  1141.                 content: createBox("Content"),
  1142.                 lines: {
  1143.                     top: createLine("Top"),
  1144.                     right: createLine("Right"),
  1145.                     bottom: createLine("Bottom"),
  1146.                     left: createLine("Left")
  1147.                 }
  1148.             };
  1149.  
  1150.             nodes.parent.appendChild(nodes.rulerH);
  1151.             nodes.parent.appendChild(nodes.rulerV);
  1152.             nodes.offset.appendChild(nodes.margin);
  1153.             nodes.margin.appendChild(nodes.border);
  1154.             nodes.border.appendChild(nodes.padding);
  1155.             nodes.padding.appendChild(nodes.content);
  1156.         }
  1157.  
  1158.         return context.boxModelHighlighter;
  1159.     },
  1160.  
  1161.     setNodesByOffsetParent: function(win, offsetParent, nodes)
  1162.     {
  1163.         var parentStyle = win.getComputedStyle(offsetParent, "");
  1164.         var parentOffset = getLTRBWH(offsetParent);
  1165.         parentOffset = applyBodyOffsets(offsetParent, parentOffset);
  1166.         var parentX = parentOffset.left + parseInt(parentStyle.borderLeftWidth);
  1167.         var parentY = parentOffset.top + parseInt(parentStyle.borderTopWidth);
  1168.         var parentW = offsetParent.offsetWidth-1;
  1169.         var parentH = offsetParent.offsetHeight-1;
  1170.  
  1171.         move(nodes.parent, parentX, parentY);
  1172.         resize(nodes.parent, parentW, parentH);
  1173.  
  1174.         if (parentX < 14)
  1175.             setClass(nodes.parent, "overflowRulerX");
  1176.         else
  1177.             removeClass(nodes.parent, "overflowRulerX");
  1178.  
  1179.         if (parentY < 14)
  1180.             setClass(nodes.parent, "overflowRulerY");
  1181.         else
  1182.             removeClass(nodes.parent, "overflowRulerY");
  1183.     }
  1184. };
  1185.  
  1186. function getNonFrameBody(elt)
  1187. {
  1188.     var body = getBody(elt.ownerDocument);
  1189.     return (body.localName && body.localName.toUpperCase() == "FRAMESET") ? null : body;
  1190. }
  1191.  
  1192. function attachStyles(context, body)
  1193. {
  1194.     var doc = body.ownerDocument;
  1195.     if (!context.highlightStyle)
  1196.         context.highlightStyle = createStyleSheet(doc, highlightCSS);
  1197.  
  1198.     if (!context.highlightStyle.parentNode || context.highlightStyle.ownerDocument != doc)
  1199.         addStyleSheet(body.ownerDocument, context.highlightStyle);
  1200. }
  1201.  
  1202. function createProxiesForDisabledElements(body)
  1203. {
  1204.     var i, rect, div,
  1205.         doc = body.ownerDocument,
  1206.         nodes = doc.getElementsByTagName("*");
  1207.  
  1208.     for(i = 0; i < nodes.length; i++)
  1209.     {
  1210.         if(nodes[i].hasAttribute("disabled") && !nodes[i].fbHasProxyElement)
  1211.         {
  1212.             rect = nodes[i].getBoundingClientRect();
  1213.  
  1214.             div = doc.createElementNS("http://www.w3.org/1999/xhtml", "div");
  1215.             div.className = "fbProxyElement";
  1216.             div.style.left = rect.left + "px";
  1217.             div.style.top = rect.top + body.scrollTop + "px";
  1218.             div.style.width = rect.width + "px";
  1219.             div.style.height = rect.height + "px";
  1220.             unwrapObject(div).firebugIgnore = true;
  1221.  
  1222.             div.fbProxyFor = nodes[i];
  1223.             nodes[i].fbHasProxyElement = true;
  1224.  
  1225.             body.appendChild(div);
  1226.         }
  1227.     }
  1228. }
  1229.  
  1230. function rgbToHex(value)
  1231. {
  1232.     return value.replace(/\brgb\((\d{1,3}),\s*(\d{1,3}),\s*(\d{1,3})\)/gi, function(_, r, g, b) {
  1233.     return '#' + ((1 << 24) + (r << 16) + (g << 8) + (b << 0)).toString(16).substr(-6).toUpperCase();
  1234.     });
  1235. }
  1236.  
  1237. function isVisibleElement(elt)
  1238. {
  1239.     var invisibleElements =
  1240.         {
  1241.             "head": true,
  1242.             "base": true,
  1243.             "basefont": true,
  1244.             "isindex": true,
  1245.             "link": true,
  1246.             "meta": true,
  1247.             "script": true,
  1248.             "style": true,
  1249.             "title": true,
  1250.             "isindex": true
  1251.         }
  1252.  
  1253.     return !invisibleElements[elt.nodeName.toLowerCase()];
  1254. }
  1255. // ************************************************************************************************
  1256.  
  1257. Firebug.registerModule(Firebug.Inspector);
  1258.  
  1259. // ************************************************************************************************
  1260.  
  1261. }});
  1262.